use crate::request_model::{RequestModel, Source};
use crate::{ReqContext, RequestContext};
use cassie_core_base::error::Error as CError;
use jsonwebtoken::errors::ErrorKind;
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, Getters, Setters, Default)]
#[getset(get = "pub", set = "pub")]
pub struct JWTToken {
  //账号id
  id: i64,
  //是否超级管理员
  super_admin: i32,
  //账号
  username: String,
  //来源
  from: Source,
  //租户编码
  agency_code: String,
  //过期时间
  exp: usize,
}

impl JWTToken {
  pub fn create_token(&self, secret: &str) -> Result<String, CError> {
    return match encode(&Header::default(), self, &EncodingKey::from_secret(secret.as_ref())) {
      Ok(t) => Ok(t),
      Err(_) => Err(CError::from("JWTToken encode fail!")), // in practice you would return the error
    };
  }

  pub fn verify(secret: &str, token: &str) -> Result<JWTToken, CError> {
    let validation = Validation::new(Algorithm::HS256);
    return match decode::<JWTToken>(&token, &DecodingKey::from_secret(secret.as_ref()), &validation) {
      Ok(c) => Ok(c.claims),
      Err(err) => match *err.kind() {
        ErrorKind::InvalidToken => return Err(CError::from("Token失效")),      // Example on how to handle a specific error
        ErrorKind::InvalidIssuer => return Err(CError::from("InvalidIssuer")), // Example on how to handle a specific error
        _ => return Err(CError::from("InvalidToken other errors")),
      },
    };
  }
}

pub fn checked_token(token: &str, jwt_secret: &str) -> Result<JWTToken, CError> {
  //check token alive
  let token = JWTToken::verify(jwt_secret, token);
  token
}

pub fn set_local(data: JWTToken, path: String) {
  let req = RequestContext::get_request_model();
  if let Some(request) = req {
    let mut request_model = request.lock().unwrap();
    request_model.setData(data, path);
  }
}

pub fn clear_local() {
  let req = RequestContext::get_request_model();
  if let Some(request) = req {
    let mut request_model = request.lock().unwrap();
    request_model.clear();
  }
}
